global loader ; making entry point visible to linker
extern main, __cxa_atexit ; _main is defined elsewhere, same with __cxa_atexit

; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum required

section .text
align 4
MultiBootHeader: ; Multiboot header
dd MAGIC
dd FLAGS
dd CHECKSUM

; reserve initial kernel stack space
STACKSIZE equ 0x4000 ; that's 16k.

extern start_ctors, end_ctors, start_dtors, end_dtors ; defined later on

loader:
mov esp, stack+STACKSIZE ; set up the stack
push ebx ; Multiboot magic number
push eax ; Multiboot info structure

static_ctors_loop: ; global variable constructor loop
mov ebx, start_ctors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_ctors
jb .body

call main ; call kernel proper
call __cxa_atexit ; call destructor

static_dtors_loop: ; global variables destructor loop
mov ebx, start_dtors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_dtors
jb .body

hlt ; halt machine should kernel return

; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global gdt_flush ; Allows the C code to link to this
extern gp ; Says that 'gp' is in another file
gdt_flush:
lgdt [gp] ; Load the GDT with our 'gp' which is a special pointer
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
flush2:
ret ; Returns back to the C++ code!

; Loads the IDT defined in '_idtp' into the processor.
; This is declared in C++ as 'extern "C" void idt_load();'
global idt_load
extern idtp
idt_load:
lidt [idtp]
ret
; make isrs visible to our c++ code and rest of the kernel
global isr0
global isr1
global isr2
global isr3
global isr4
global isr5
global isr6
global isr7
global isr8
global isr9
global isr10
global isr11
global isr12
global isr13
global isr14
global isr15
global isr16
global isr17
global isr18
global isr19
global isr20
global isr21
global isr22
global isr23
global isr24
global isr25
global isr26
global isr27
global isr28
global isr29
global isr30
global isr31

; 0: Divide By Zero Exception
isr0:
cli
push byte 0
push byte 0
jmp isr_common_stub

; 1: Debug Exception
isr1:
cli
push byte 0
push byte 1
jmp isr_common_stub

; 2: Non Maskable Interrupt Exception
isr2:
cli
push byte 0
push byte 2
jmp isr_common_stub

; 3: Int 3 Exception
isr3:
cli
push byte 0
push byte 3
jmp isr_common_stub

; 4: INTO Exception
isr4:
cli
push byte 0
push byte 4
jmp isr_common_stub

; 5: Out of Bounds Exception
isr5:
cli
push byte 0
push byte 5
jmp isr_common_stub

; 6: Invalid Opcode Exception
isr6:
cli
push byte 0
push byte 6
jmp isr_common_stub

; 7: Coprocessor Not Available Exception
isr7:
cli
push byte 0
push byte 7
jmp isr_common_stub

; 8: Double Fault Exception (With Error Code!)
isr8:
cli
push byte 8
jmp isr_common_stub

; 9: Coprocessor Segment Overrun Exception
isr9:
cli
push byte 0
push byte 9
jmp isr_common_stub

; 10: Bad TSS Exception (With Error Code!)
isr10:
cli
push byte 10
jmp isr_common_stub

; 11: Segment Not Present Exception (With Error Code!)
isr11:
cli
push byte 11
jmp isr_common_stub

; 12: Stack Fault Exception (With Error Code!)
isr12:
cli
push byte 12
jmp isr_common_stub

; 13: General Protection Fault Exception (With Error Code!)
isr13:
cli
push byte 13
jmp isr_common_stub


; 14: Page Fault Exception (With Error Code!)
isr14:
cli
push byte 14
jmp isr_common_stub

; 15: Reserved Exception
isr15:
cli
push byte 0
push byte 15
jmp isr_common_stub

; 16: Floating Point Exception
isr16:
cli
push byte 0
push byte 16
jmp isr_common_stub

; 17: Alignment Check Exception
isr17:
cli
push byte 0
push byte 17
jmp isr_common_stub

; 18: Machine Check Exception
isr18:
cli
push byte 0
push byte 18
jmp isr_common_stub

; 19: Reserved
isr19:
cli
push byte 0
push byte 19
jmp isr_common_stub

; 20: Reserved
isr20:
cli
push byte 0
push byte 20
jmp isr_common_stub

; 21: Reserved
isr21:
cli
push byte 0
push byte 21
jmp isr_common_stub

; 22: Reserved
isr22:
cli
push byte 0
push byte 22
jmp isr_common_stub

; 23: Reserved
isr23:
cli
push byte 0
push byte 23
jmp isr_common_stub

; 24: Reserved
isr24:
cli
push byte 0
push byte 24
jmp isr_common_stub

; 25: Reserved
isr25:
cli
push byte 0
push byte 25
jmp isr_common_stub

; 26: Reserved
isr26:
cli
push byte 0
push byte 26
jmp isr_common_stub

; 27: Reserved
isr27:
cli
push byte 0
push byte 27
jmp isr_common_stub

; 28: Reserved
isr28:
cli
push byte 0
push byte 28
jmp isr_common_stub

; 29: Reserved
isr29:
cli
push byte 0
push byte 29
jmp isr_common_stub

; 30: Reserved
isr30:
cli
push byte 0
push byte 30
jmp isr_common_stub

; 31: Reserved
isr31:
cli
push byte 0
push byte 31
jmp isr_common_stub


; We call a C++ function in here. We need to let the assembler know
; that 'fault_handler' exists in another file
extern fault_handler

; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, fault_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
; same thing as isrs but with irqs
global irq0
global irq1
global irq2
global irq3
global irq4
global irq5
global irq6
global irq7
global irq8
global irq9
global irq10
global irq11
global irq12
global irq13
global irq14
global irq15

; 32: IRQ0
irq0:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 32
jmp irq_common_stub

; 32: IRQ0
irq1:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 33
jmp irq_common_stub

; 32: IRQ0
irq2:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 34
jmp irq_common_stub

; 32: IRQ0
irq3:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 35
jmp irq_common_stub


; 32: IRQ0
irq4:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 36
jmp irq_common_stub

; 32: IRQ0
irq5:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 37
jmp irq_common_stub

; 32: IRQ0
irq6:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 38
jmp irq_common_stub

; 32: IRQ0
irq7:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 39
jmp irq_common_stub

; 32: IRQ0
irq8:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 40
jmp irq_common_stub

; 32: IRQ0
irq9:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 41
jmp irq_common_stub

; 32: IRQ0
irq10:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 42
jmp irq_common_stub

; 32: IRQ0
irq11:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 43
jmp irq_common_stub

; 32: IRQ0
irq12:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 44
jmp irq_common_stub

; 32: IRQ0
irq13:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 45
jmp irq_common_stub

; 32: IRQ0
irq14:
cli
push byte 0 ; Note that these don't push an error code on the stack:
; We need to push a dummy error code
push byte 46
jmp irq_common_stub

; 47: IRQ15
irq15:
cli
push byte 0
push byte 47
jmp irq_common_stub

extern irq_handler

; This is a stub that we have created for IRQ based ISRs. This calls
; 'irq_handler' in our C++ code. We need to create this in an 'irq.c'
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, irq_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret

section .bss
align 4
stack:
resb STACKSIZE ; reserve 16k stack on a doubleword boundary